﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using xplang.parse;
using xplang.errors;
using xplang.contexts;
using xplang.abstractions;
using System.Globalization;
using xplang.run;

namespace xplang.fs
{
  public abstract class FileSystem
  {

    public FileSystem()
    {
      compilers.Add("class", (source, context, name) => new CompilerForClass(source,context,name));
    }

    public static readonly IFormatProvider formatProvider = CultureInfo.InvariantCulture;

    public abstract SourceCode get(String fullClassName);
    public abstract void close();
    public abstract IEnumerable<SourceCode> listFiles(String path);
    //        abstract SourceCode delete(String fullClassName);

    public readonly Dictionary<string, GetCompiler> compilers = new Dictionary<string, GetCompiler>();
    private readonly List<CompilerForFiles> compiling = new List<CompilerForFiles>();
    public int compileState = 0;

    public void build(Context root)
    {
      compileState = 0;
      Error.buildErrors.Clear();
      IEnumerable<SourceCode> files = listFiles("/");
      compiling.Clear();
      CompilerForFiles c;
      foreach (SourceCode s in files)
      {
        c = compileHeader(root, s);
        if (c != null)
          compiling.Add(c);
      }
      int n = compiling.Count;
      for (compileState = 1; compileState <= 3; compileState++)
        for (int i = 0; i < n; i++)
          compiling[i].compile(compileState);
    }

    public void advanceCompilation(Object used)
    {
      CompilerForFiles comp = compiling.SingleOrDefault(c => c.declaring == used);
      if (comp != null)
        comp.compile(compileState + 1);
    }

    public CompilerForFiles compileHeader(Context context, SourceCode source)
    {
      SourceReader r = new SourceReader(source);
      r.nextToken();
      Token type = r.current;
      if (type.type != TokenType.ttIdentifier)
      {
        new SuportedTypesError(type);
        return null;
      }

      GetCompiler getCompiler = compilers[type.text];
      if (getCompiler == null)
      {
        new SuportedTypesError(type);
        return null;
      }
      r.nextToken();
      Token name = r.current;
      if (name.type != TokenType.ttIdentifier)
      {
        new IdentifierExpectedError(type);
        return null;
      }
      r.nextToken();
      CompilerForFiles compiler = getCompiler(r, context, name);
      compiler.current = r.current;
      return compiler;
    }
  }
}
